# coding:utf-8

from datetime import datetime, date
from typing import List, Optional
from pathlib import Path

from pydantic import BaseModel, ValidationError, constr
from sqlalchemy import Column, Integer, String
from sqlalchemy.dialects.postgresql import ARRAY
from sqlalchemy.ext.declarative import declarative_base

print('\033[31m 1.-----BaseModel-----\033[0m')


class User(BaseModel):
    id: int
    name: str = "John Snow"
    signup_ts: Optional[datetime] = None
    friends: List[int] = []


external_data = {
    "id": "123",
    "signup_ts": "2021-12-22 10:15",
    "friends": [1, 2, 3, '4'],
}

user = User(**external_data)
print(user.id, user.friends)
print(repr(user.signup_ts))
print(user.dict())
# TODO
# why not output signup_ts

print('\033[31m 2.-----validate fail-----\033[0m')
try:
    user = User(id=1, signup_ts=datetime.today(), friends=[1, 2, 'not number'])
except ValidationError as e:
    print(e.json())

print('\033[31m 3.-----property-----\033[0m')
print(user.dict())
print(user.json())
# ODO: shadow copy why friends data exist
print(user.copy())
print(User.parse_obj(obj=external_data))
print(User.parse_raw('{"id": 123, "name": "John Snow", "friends": [1, 2, 3, 9]}'))


# TODO can not create file automatically
path = Path('pydantic_tutorial.json')
path.write_text('{"id": 123, "name": "John Snow", "friends": [1, 2, 3, 78]}')
print(User.parse_file(path))

print(user.schema())
print(user.schema_json())
# {'title': 'User', 'type': 'object', 'properties': {'id': {'title': 'Id', 'type': 'integer'}, 'name': {'title': 'Name', 'default': 'John Snow', 'type': 'string'}, 'signup_ts': {'title': 'Signup Ts', 'type': 'string', 'format': 'date-time'}, 'friends': {'title': 'Friends', 'default': [], 'type': 'array', 'items': {'type': 'integer'}}}, 'required': ['id']}
# {"title": "User", "type": "object", "properties": {"id": {"title": "Id", "type": "integer"}, "name": {"title": "Name", "default": "John Snow", "type": "string"}, "signup_ts": {"title": "Signup Ts", "type": "string", "format": "date-time"}, "friends": {"title": "Friends", "default": [], "type": "array", "items": {"type": "integer"}}}, "required": ["id"]}

user_data = {"id": 'error', "name": "John Snow", "friends": [1, 2, 3, 'not']}
print(User.construct(**user_data))  # cannot validate

print(User.__fields__.keys())
# dict_keys(['id', 'name', 'signup_ts', 'friends'])


print('\033[31m 4.-----递归-----\033[0m')


class Sound(BaseModel):
    sound: str


class Dog(BaseModel):
    birthday: date
    weight: Optional[float]
    sound: List[Sound]


dog = Dog(birthday=date.today(), weight=6.66, sound=[{'sound': 'wang wang'}, {'sound': 'ying ying'}])
print(dog.dict())

print('\033[31m 5.-----ORM-----\033[0m')

Base = declarative_base()


class CompanyOrm(Base):
    __tablename__ = 'companies'
    id = Column(Integer, primary_key=True, nullable=False)
    public_key = Column(String(20), index=True, nullable=False, unique=True)
    name = Column(String(63), unique=True)
    domains = Column(ARRAY(String(255)))


class CompanyModel(BaseModel):
    id: int
    public_key: constr(max_length=20)
    name: constr(max_length=63)
    domains: List[constr(max_length=255)]

    class Config:
        orm_mode = True


co_orm = CompanyOrm(
    id=123,
    public_key='key',
    name='name',
    domains=['imooc.com', 'baidu.com']
)

print(CompanyModel.from_orm(co_orm))

print('\033[31m 6.-----field types-----\033[0m')
